package com.photoeditor.demo.ui.widget.image.mixer;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

import com.common.base.ComponentContext;
import com.common.base.utils.DeviceUtils;
import com.photoeditor.R;
import com.photoeditor.demo.model.bean.EditEmojiBean;
import com.photoeditor.demo.model.image.collage.templet.Line;
import com.photoeditor.demo.model.image.collage.util.MathUtil;
import com.photoeditor.demo.model.image.util.ImageHelper;
import com.photoeditor.demo.util.TouchUtil;

/**
 * Created by Lai on 2017/12/26 0026.
 */

public class ImageMixerView extends View {

    public final static int RADIUS = ComponentContext.getContext().getResources().getDimensionPixelSize(R.dimen.image_edit_operation_button_size);
    public final static int SMALLEST_SIZE = ComponentContext.getContext().getResources().getDimensionPixelSize(R.dimen.scale_smallest_size);
    public final static float MAX_SIZE = ImageHelper.SCREEN_WIDTH * 3f;
    public final static int DEFAULT_SIZE = DeviceUtils.dip2px(150);

    private Bitmap mBottomBitmap;
    private Bitmap mUpperBitmap;

    private Bitmap mCacheBitmap;
    private RectF mBottomCacheRect;
    private RectF mUpperCacheRect;

    private boolean mIsInit = false;

    private Rect mRect;

    private RectF mBottomBitmapRect;
    private RectF mUpperBitmapRect;
    private RectF mUpperBitmapOriginalRect;
    private float mDegree = 0;
    private Matrix mMatrix;

    private Paint mBitmapPaint;

    private Drawable mOperationDrawable;
    private Drawable mDeleteDrawable;
    private Drawable mTouchLeftDrawable;
    private Drawable mTouchRightDrawable;
    private Drawable mTouchTopDrawable;
    private Drawable mTouchBottomDrawable;
    private Paint mBoundPaint;

    private int mMode;
    public static final int MODE_NONE = -1;
    public static final int MODE_MOVE = 2;
    public static final int MODE_SCALE_OR_ROTATION_OR_MOVE = 3;
    private static final int MODE_SCALE_OR_ROTATION = 6;
    public static final int MODE_SELECT_LEFT_OPERATION = 7;
    public static final int MODE_SELECT_RIGHT_OPERATION = 8;
    public static final int MODE_SELECT_TOP_OPERATION = 9;
    public static final int MODE_SELECT_BOTTOM_OPERATION = 10;
    private RectF mOperateRect;
    private RectF mLeftRect;
    private RectF mRightRect;
    private RectF mTopRect;
    private RectF mBottomRect;
    private RectF mDeleteRect;
    private float mDownX, mDownY;
    private boolean mIsClickDelete;
    private boolean mClick;
    private boolean mEraseMode = false;

    private PorterDuffXfermode mXfermode;
    private boolean mDrawBottomAgain = false;

    private static final float TouchSlop = ViewConfiguration.get(ComponentContext.getContext()).getScaledTouchSlop();

    public ImageMixerView(Context context) {
        this(context, null);
    }

    public ImageMixerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ImageMixerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize();
    }

    private void initialize() {
        setLayerType(LAYER_TYPE_SOFTWARE, null);

        mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

        mOperationDrawable = getResources().getDrawable(R.drawable.ic_rotate);
        mDeleteDrawable = getResources().getDrawable(R.drawable.ic_close_emoji);
        mTouchLeftDrawable = getResources().getDrawable(R.drawable.ic_touch_move_left);
        mTouchRightDrawable = getResources().getDrawable(R.drawable.ic_touch_move_right);
        mTouchTopDrawable = getResources().getDrawable(R.drawable.ic_touch_move_top);
        mTouchBottomDrawable = getResources().getDrawable(R.drawable.ic_touch_move_bottom);

        mBoundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBoundPaint.setColor(getResources().getColor(R.color.image_edit_text_bound_color));
        mBoundPaint.setAntiAlias(true);
        mBoundPaint.setStrokeWidth(DeviceUtils.dip2px(ComponentContext.getContext(), 1));
        mBoundPaint.setStyle(Paint.Style.STROKE);

        mOperateRect = new RectF();
        mLeftRect = new RectF();
        mRightRect = new RectF();
        mTopRect = new RectF();
        mBottomRect = new RectF();
        mDeleteRect = new RectF();

        mUpperBitmapOriginalRect = new RectF();
        mBottomCacheRect = new RectF();
        mUpperCacheRect = new RectF();

        mMatrix = new Matrix();

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (!mIsInit) {
            mRect = new Rect(0, 0, right - left, bottom - top);
            if (mBottomBitmapRect == null) {
                mBottomBitmapRect = new RectF(mRect);
            } else {
                float width = mBottomBitmapRect.width();
                float height = mBottomBitmapRect.height();
                mBottomBitmapRect.left = mRect.centerX() - width / 2;
                mBottomBitmapRect.top = mRect.centerY() - height / 2;
                mBottomBitmapRect.right = mBottomBitmapRect.left + width;
                mBottomBitmapRect.bottom = mBottomBitmapRect.top + height;
            }
            mBottomCacheRect.set(mBottomBitmapRect);
            if (mUpperBitmapRect == null) {
                mUpperBitmapRect = new RectF(mRect);
            } else {
                float width = mUpperBitmapRect.width();
                float height = mUpperBitmapRect.height();
                mUpperBitmapRect.left = mRect.centerX() - width / 2;
                mUpperBitmapRect.top = mRect.centerY() - height / 2;
                mUpperBitmapRect.right = mUpperBitmapRect.left + width;
                mUpperBitmapRect.bottom = mUpperBitmapRect.top + height;
                updateOtherRect();
            }
            mUpperBitmapOriginalRect.set(mUpperBitmapRect);
            mIsInit = true;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!mIsInit || mBottomBitmap == null || mBottomBitmap.isRecycled()) {
            return;
        }
        canvas.clipRect(mBottomBitmapRect);
        int layer = -1;
        if (mDrawBottomAgain) {
            canvas.drawBitmap(mBottomBitmap, null, mBottomCacheRect, mBitmapPaint);
            layer = canvas.saveLayer(mBottomCacheRect.left, mBottomCacheRect.top, mBottomCacheRect.right, mBottomCacheRect.bottom, null, Canvas.ALL_SAVE_FLAG);
        }
        canvas.drawBitmap(mBottomBitmap, null, mBottomCacheRect, mBitmapPaint);

        mBitmapPaint.setXfermode(mXfermode);
        if (mEraseMode) {
            if (mCacheBitmap != null && !mCacheBitmap.isRecycled()) {
                canvas.save();
                canvas.clipRect(mBottomCacheRect);
                canvas.rotate(mDegree, mUpperCacheRect.centerX(), mUpperCacheRect.centerY());
                canvas.drawBitmap(mCacheBitmap, null, mUpperCacheRect, mBitmapPaint);
                canvas.restore();
            }
        } else {
            if (mUpperBitmap != null && !mUpperBitmap.isRecycled()) {
                canvas.save();
                canvas.rotate(mDegree, mUpperBitmapRect.centerX(), mUpperBitmapRect.centerY());
                canvas.drawBitmap(mUpperBitmap, null, mUpperBitmapRect, mBitmapPaint);
                canvas.drawRect(mUpperBitmapRect, mBoundPaint);
                if (mMode == MODE_SCALE_OR_ROTATION) {
                    mOperationDrawable.setBounds((int) (mOperateRect.left - mRect.left + 0.5f), (int) (mOperateRect.top - mRect.top + 0.5f), (int) (mOperateRect.right - mRect.left + 0.5f), (int) (mOperateRect.bottom - mRect.top + 0.5f));
                    mOperationDrawable.draw(canvas);
                } else if (mMode == MODE_SELECT_LEFT_OPERATION || mMode == MODE_SELECT_RIGHT_OPERATION) {
                    mTouchLeftDrawable.setBounds((int) (mLeftRect.left - mRect.left + 0.5f), (int) (mLeftRect.top - mRect.top + 0.5f), (int) (mLeftRect.right - mRect.left + 0.5f), (int) (mLeftRect.bottom - mRect.top + 0.5f));
                    mTouchLeftDrawable.draw(canvas);
                    mTouchRightDrawable.setBounds((int) (mRightRect.left - mRect.left + 0.5f), (int) (mRightRect.top - mRect.top + 0.5f), (int) (mRightRect.right - mRect.left + 0.5f), (int) (mRightRect.bottom - mRect.top + 0.5f));
                    mTouchRightDrawable.draw(canvas);
                } else if (mMode == MODE_SELECT_TOP_OPERATION || mMode == MODE_SELECT_BOTTOM_OPERATION) {
                    mTouchTopDrawable.setBounds((int) (mTopRect.left - mRect.left + 0.5f), (int) (mTopRect.top - mRect.top + 0.5f), (int) (mTopRect.right - mRect.left + 0.5f), (int) (mTopRect.bottom - mRect.top + 0.5f));
                    mTouchTopDrawable.draw(canvas);
                    mTouchBottomDrawable.setBounds((int) (mBottomRect.left - mRect.left + 0.5f), (int) (mBottomRect.top - mRect.top + 0.5f), (int) (mBottomRect.right - mRect.left + 0.5f), (int) (mBottomRect.bottom - mRect.top + 0.5f));
                    mTouchBottomDrawable.draw(canvas);
                } else {
                    mOperationDrawable.setBounds((int) (mOperateRect.left - mRect.left + 0.5f), (int) (mOperateRect.top - mRect.top + 0.5f), (int) (mOperateRect.right - mRect.left + 0.5f), (int) (mOperateRect.bottom - mRect.top + 0.5f));
                    mOperationDrawable.draw(canvas);
                    mDeleteDrawable.setBounds((int) (mDeleteRect.left - mRect.left + 0.5f), (int) (mDeleteRect.top - mRect.top + 0.5f), (int) (mDeleteRect.right - mRect.left + 0.5f), (int) (mDeleteRect.bottom - mRect.top + 0.5f));
                    mDeleteDrawable.draw(canvas);
                    mTouchLeftDrawable.setBounds((int) (mLeftRect.left - mRect.left + 0.5f), (int) (mLeftRect.top - mRect.top + 0.5f), (int) (mLeftRect.right - mRect.left + 0.5f), (int) (mLeftRect.bottom - mRect.top + 0.5f));
                    mTouchLeftDrawable.draw(canvas);
                    mTouchRightDrawable.setBounds((int) (mRightRect.left - mRect.left + 0.5f), (int) (mRightRect.top - mRect.top + 0.5f), (int) (mRightRect.right - mRect.left + 0.5f), (int) (mRightRect.bottom - mRect.top + 0.5f));
                    mTouchRightDrawable.draw(canvas);
                    mTouchTopDrawable.setBounds((int) (mTopRect.left - mRect.left + 0.5f), (int) (mTopRect.top - mRect.top + 0.5f), (int) (mTopRect.right - mRect.left + 0.5f), (int) (mTopRect.bottom - mRect.top + 0.5f));
                    mTouchTopDrawable.draw(canvas);
                    mTouchBottomDrawable.setBounds((int) (mBottomRect.left - mRect.left + 0.5f), (int) (mBottomRect.top - mRect.top + 0.5f), (int) (mBottomRect.right - mRect.left + 0.5f), (int) (mBottomRect.bottom - mRect.top + 0.5f));
                    mTouchBottomDrawable.draw(canvas);
                }
                canvas.restore();
            }
        }
        mBitmapPaint.setXfermode(null);
        if (layer != -1) {
            canvas.restoreToCount(layer);
        }
    }

    float oldDist = 1f;
    float oldRotation = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!mIsInit || mUpperBitmap == null || mUpperBitmap.isRecycled() || mEraseMode) {
            return false;
        }
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                if (event.getPointerCount() != 1) {
                    break;
                }
                mDownX = event.getX();
                mDownY = event.getY();
                mClick = true;
                float[] dst = new float[2];
                getRotateMatrix().mapPoints(dst, new float[]{mDownX, mDownY});

                float d = (float) Math.sqrt((dst[0] - mUpperBitmapRect.right) * (dst[0] - mUpperBitmapRect.right) + (dst[1] - mUpperBitmapRect.bottom) * (dst[1] - mUpperBitmapRect.bottom));
                float deleteDistance = (float) Math.sqrt((dst[0] - mUpperBitmapRect.left) * (dst[0] - mUpperBitmapRect.left) + (dst[1] - mUpperBitmapRect.top) * (dst[1] - mUpperBitmapRect.top));
                if (mUpperBitmapRect.contains(dst[0], dst[1])) {
                    if (d <= EditEmojiBean.RADIUS + TouchUtil.TOUCH_BUTTON_OFFSET) {//按钮
                        mMode = MODE_SCALE_OR_ROTATION;
                    } else if (deleteDistance <= EditEmojiBean.RADIUS + TouchUtil.TOUCH_BUTTON_OFFSET) {
                        mIsClickDelete = true;
                    } else if (mLeftRect.contains(dst[0], dst[1])) {
                        mMode = MODE_SELECT_LEFT_OPERATION;
                    } else if (mRightRect.contains(dst[0], dst[1])) {
                        mMode = MODE_SELECT_RIGHT_OPERATION;
                    } else if (mTopRect.contains(dst[0], dst[1])) {
                        mMode = MODE_SELECT_TOP_OPERATION;
                    } else if (mBottomRect.contains(dst[0], dst[1])) {
                        mMode = MODE_SELECT_BOTTOM_OPERATION;
                    } else {
                        mMode = MODE_MOVE;
                    }
                    refresh();
                    return true;
                } else {
                    if (d <= EditEmojiBean.RADIUS + TouchUtil.TOUCH_BUTTON_OFFSET) {//按钮
                        refresh();
                        mMode = MODE_SCALE_OR_ROTATION;
                        return true;
                    } else if (deleteDistance <= EditEmojiBean.RADIUS + TouchUtil.TOUCH_BUTTON_OFFSET) {
                        refresh();
                        mIsClickDelete = true;
                        return true;
                    } else if (mLeftRect.contains(dst[0], dst[1])) {
                        refresh();
                        mMode = MODE_SELECT_LEFT_OPERATION;
                        return true;
                    } else if (mRightRect.contains(dst[0], dst[1])) {
                        refresh();
                        mMode = MODE_SELECT_RIGHT_OPERATION;
                        return true;
                    } else if (mTopRect.contains(dst[0], dst[1])) {
                        refresh();
                        mMode = MODE_SELECT_TOP_OPERATION;
                        return true;
                    } else if (mBottomRect.contains(dst[0], dst[1])) {
                        refresh();
                        mMode = MODE_SELECT_BOTTOM_OPERATION;
                        return true;
                    } else {
                        mMode = MODE_NONE;
                    }
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                oldRotation = rotation(event);
                float[] dst1 = new float[2];
                getRotateMatrix().mapPoints(dst1, new float[]{event.getX(0), event.getY(0)});
                mClick = true;
                if (mUpperBitmapRect.contains(dst1[0], dst1[1])) {
                    mMode = MODE_SCALE_OR_ROTATION_OR_MOVE;
                    refresh();
                    return true;
                }
                mMode = MODE_NONE;
                refresh();
                break;
            case MotionEvent.ACTION_MOVE:
                if (event.getPointerCount() > 1 && mMode == MODE_SCALE_OR_ROTATION_OR_MOVE) {
                    float newDist = spacing(event);
                    if (mClick && Math.abs(newDist - oldDist) <= TouchSlop) {
                        return true;
                    }
                    mClick = false;
                    mIsClickDelete = false;
                    float rotation = rotation(event) - oldRotation;
                    float scale = newDist / oldDist;
                    setDegree(rotation);
                    setScale(scale);
                    oldRotation = rotation(event);
                    oldDist = newDist;
                    refresh();
                    return true;
                } else {
                    float nowX = event.getX();
                    float nowY = event.getY();
                    float dx = nowX - mDownX;
                    float dy = nowY - mDownY;
                    if (mClick && Math.abs(dx) <= TouchSlop
                            && Math.abs(dy) <= TouchSlop) {
                        return true;
                    }
                    mClick = false;
                    mIsClickDelete = false;
                    float[] dst2;
                    float[] sda;
                    if (mMode == MODE_MOVE) {
                        move(dx, dy);
                        refresh();
                    } else if (mMode == MODE_SCALE_OR_ROTATION) {
                        dealScaleAndRotation(mDownX, mDownY, nowX, nowY);
                        refresh();
                    } else if (mMode == MODE_SELECT_LEFT_OPERATION) {
                        dst2 = new float[2];
                        getRotateMatrix().mapPoints(dst2, new float[]{mDownX, mDownY});

                        sda = new float[2];
                        getRotateMatrix().mapPoints(sda, new float[]{nowX, nowY});

                        dx = sda[0] - dst2[0];
                        operationLeft(dx);
                        refresh();
                    } else if (mMode == MODE_SELECT_RIGHT_OPERATION) {

                        dst2 = new float[2];
                        getRotateMatrix().mapPoints(dst2, new float[]{mDownX, mDownY});

                        sda = new float[2];
                        getRotateMatrix().mapPoints(sda, new float[]{nowX, nowY});

                        dx = sda[0] - dst2[0];
                        operationRight(dx);
                        refresh();
                    } else if (mMode == MODE_SELECT_TOP_OPERATION) {

                        dst2 = new float[2];
                        getRotateMatrix().mapPoints(dst2, new float[]{mDownX, mDownY});

                        sda = new float[2];
                        getRotateMatrix().mapPoints(sda, new float[]{nowX, nowY});

                        dy = sda[1] - dst2[1];

                        operationTop(dy);
                        refresh();
                    } else if (mMode == MODE_SELECT_BOTTOM_OPERATION) {

                        dst2 = new float[2];
                        getRotateMatrix().mapPoints(dst2, new float[]{mDownX, mDownY});

                        sda = new float[2];
                        getRotateMatrix().mapPoints(sda, new float[]{nowX, nowY});

                        dy = sda[1] - dst2[1];

                        operationBottom(dy);
                        refresh();
                    }
                    mDownX = nowX;
                    mDownY = nowY;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                boolean ret = false;
                if (mMode == MODE_MOVE || mMode == MODE_SCALE_OR_ROTATION
                        || mMode == MODE_SELECT_LEFT_OPERATION
                        || mMode == MODE_SELECT_RIGHT_OPERATION
                        || mMode == MODE_SELECT_TOP_OPERATION
                        || mMode == MODE_SELECT_BOTTOM_OPERATION
                        || mMode == MODE_SCALE_OR_ROTATION_OR_MOVE) {
                    ret = true;
                }

                if (mIsClickDelete) {
                    mIsClickDelete = false;
                    deleteUpperBitmap();
                }

                mClick = false;
                mMode = MODE_NONE;
                refresh();
                return ret;
        }
        return super.onTouchEvent(event);
    }

    private void deleteUpperBitmap() {
        if (mUpperBitmap != null && !mUpperBitmap.isRecycled()) {
            mUpperBitmap.recycle();
            mUpperBitmap = null;
        }
        mDegree = 0;
        invalidate();
        if (mOnImageDeleteListener != null) {
            mOnImageDeleteListener.onDelete();
        }
    }

    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);
        return (float) Math.toDegrees(radians);
    }

    public void refresh() {
        invalidate();
    }

    public void refreshBitmap(Bitmap bitmap, RectF upperRectF, RectF bottomRect) {
        mCacheBitmap = bitmap;
        mUpperCacheRect.set(upperRectF);
        mBottomCacheRect.set(bottomRect);
        refresh();
    }

    public void setBitmaps(Bitmap bottomBitmap, Bitmap upperBitmap) {
        mBottomBitmap = bottomBitmap;
        mUpperBitmap = upperBitmap;
        updateUpperRect(upperBitmap.getWidth(), upperBitmap.getHeight());
        refresh();
    }

    public void replaceBitmap(Bitmap upperBitmap) {
        if (mUpperBitmap != null && !mUpperBitmap.isRecycled()) {
            mUpperBitmap.recycle();
            mUpperBitmap = null;
        }
        mUpperBitmap = upperBitmap;
        mDegree = 0;
        mXfermode = null;
        updateUpperRect(upperBitmap.getWidth(), upperBitmap.getHeight());
        refresh();
    }

    public void replaceBitmapFromErase(Bitmap bitmap) {
        if (bitmap != null && !bitmap.isRecycled()) {
            if (mUpperBitmap != null && !mUpperBitmap.isRecycled()) {
                mUpperBitmap.recycle();
                mUpperBitmap = null;
            }
            mUpperBitmap = bitmap;
        }
    }

    public void intoEraseMode(boolean into) {
        mEraseMode = into;
        if (!mEraseMode) {
            mBottomCacheRect.set(mBottomBitmapRect);
            mUpperCacheRect.setEmpty();
            if (mCacheBitmap != null && !mCacheBitmap.isRecycled()) {
                mCacheBitmap.recycle();
                mCacheBitmap = null;
            }
            invalidate();
        }
    }

    public void updateBottomRect(int width, int height) {
        if (mIsInit) {
            mBottomBitmapRect.left = mRect.centerX() - width / 2;
            mBottomBitmapRect.top = mRect.centerY() - height / 2;
            mBottomBitmapRect.right = mBottomBitmapRect.left + width;
            mBottomBitmapRect.bottom = mBottomBitmapRect.top + height;
        } else {
            mBottomBitmapRect = new RectF(0, 0, width, height);
        }
        mBottomCacheRect.set(mBottomBitmapRect);
    }

    public void updateUpperRect(int sWidth, int sHeight) {
        float width = sWidth, height = sHeight;
        if (width > DEFAULT_SIZE) {
            width = DEFAULT_SIZE;
            height = DEFAULT_SIZE * sHeight * 1.0f / sWidth;
        }
        if (mIsInit) {
            mUpperBitmapRect.left = mRect.centerX() - width / 2;
            mUpperBitmapRect.top = mRect.centerY() - height / 2;
            mUpperBitmapRect.right = mUpperBitmapRect.left + width;
            mUpperBitmapRect.bottom = mUpperBitmapRect.top + height;
            updateOtherRect();
        } else {
            mUpperBitmapRect = new RectF(0, 0, width, height);
        }
        mUpperBitmapOriginalRect.set(mUpperBitmapRect);
    }

    public void updateOtherRect() {
        mOperateRect.left = mUpperBitmapRect.right - RADIUS;
        mOperateRect.top = mUpperBitmapRect.bottom - RADIUS;
        mOperateRect.right = mUpperBitmapRect.right + RADIUS;
        mOperateRect.bottom = mUpperBitmapRect.bottom + RADIUS;

        mDeleteRect.left = mUpperBitmapRect.left - RADIUS;
        mDeleteRect.top = mUpperBitmapRect.top - RADIUS;
        mDeleteRect.right = mUpperBitmapRect.left + RADIUS;
        mDeleteRect.bottom = mUpperBitmapRect.top + RADIUS;

        mLeftRect.left = mUpperBitmapRect.left - RADIUS;
        mLeftRect.top = (mUpperBitmapRect.top + mUpperBitmapRect.bottom) / 2 - RADIUS;
        mLeftRect.right = mUpperBitmapRect.left + RADIUS;
        mLeftRect.bottom = (mUpperBitmapRect.top + mUpperBitmapRect.bottom) / 2 + RADIUS;

        mRightRect.left = mUpperBitmapRect.right - RADIUS;
        mRightRect.top = (mUpperBitmapRect.top + mUpperBitmapRect.bottom) / 2 - RADIUS;
        mRightRect.right = mUpperBitmapRect.right + RADIUS;
        mRightRect.bottom = (mUpperBitmapRect.top + mUpperBitmapRect.bottom) / 2 + RADIUS;

        mTopRect.left = (mUpperBitmapRect.left + mUpperBitmapRect.right) / 2 - RADIUS;
        mTopRect.top = mUpperBitmapRect.top - RADIUS;
        mTopRect.right = (mUpperBitmapRect.left + mUpperBitmapRect.right) / 2 + RADIUS;
        mTopRect.bottom = mUpperBitmapRect.top + RADIUS;

        mBottomRect.left = (mUpperBitmapRect.left + mUpperBitmapRect.right) / 2 - RADIUS;
        mBottomRect.top = mUpperBitmapRect.bottom - RADIUS;
        mBottomRect.right = (mUpperBitmapRect.left + mUpperBitmapRect.right) / 2 + RADIUS;
        mBottomRect.bottom = mUpperBitmapRect.bottom + RADIUS;
    }

    public void reset() {
        mDegree = 0;
        mCacheBitmap = null;
        mUpperBitmap = null;
        mCacheBitmap = null;
        mXfermode = null;
    }

    public Matrix getRotateMatrix() {
        mMatrix.setRotate(-mDegree, mUpperBitmapRect.centerX(), mUpperBitmapRect.centerY());
        return mMatrix;
    }

    private void dealScaleAndRotation(float downX, float downY, float nowX, float nowY) {
        float px = mUpperBitmapRect.centerX();
        float py = mUpperBitmapRect.centerY();

        float x1 = downX - px;
        float y1 = downY - py;

        float x2 = nowX - px;
        float y2 = nowY - py;

        float scale = (float) (Math.sqrt(x2 * x2 + y2 * y2) / Math.sqrt(x1 * x1 + y1 * y1));

        double degree1 = MathUtil.getDegree(px, py, downX, downY, px + 1, py);
        double degree2 = MathUtil.getDegree(px, py, nowX, nowY, px + 1, py);

        int location1 = MathUtil.getLocation(x1, y1);
        int location2 = MathUtil.getLocation(x2, y2);

        if (location2 == 1 || location2 == 2) {
            degree2 = -degree2;
        }

        if (location1 == 1 || location1 == 2) {
            degree1 = -degree1;
        }

        double degree = degree2 - degree1;

        setScale(scale);
        setDegree((float) degree);
    }

    private void move(float dx, float dy) {
        mUpperBitmapRect.offset(dx, dy);
        mOperateRect.offset(dx, dy);
        mDeleteRect.offset(dx, dy);
        mLeftRect.offset(dx, dy);
        mRightRect.offset(dx, dy);
        mTopRect.offset(dx, dy);
        mBottomRect.offset(dx, dy);
    }

    public void setDegree(float degree) {
        mDegree += degree;
    }

    public void setScale(float scale) {
        Matrix m = new Matrix();
        m.setScale(scale, scale, mUpperBitmapRect.centerX(), mUpperBitmapRect.centerY());
        RectF rect = new RectF();
        m.mapRect(rect, mUpperBitmapRect);
        if (rect.width() >= SMALLEST_SIZE && rect.width() <= MAX_SIZE) {
            mUpperBitmapRect = rect;
            updateOtherRect();
        }
    }

    public void operationLeft(float dx) {
        float leftTopPoint[] = new float[]{mUpperBitmapRect.left, mUpperBitmapRect.top};
        float rightTopPoint[] = new float[]{mUpperBitmapRect.right, mUpperBitmapRect.top};
        float leftbottomPoint[] = new float[]{mUpperBitmapRect.left, mUpperBitmapRect.bottom};
        float rightBottomPoint[] = new float[]{mUpperBitmapRect.right, mUpperBitmapRect.bottom};

        float leftTopPointMap[] = new float[2];
        float rightTopPointMap[] = new float[2];
        float leftbottomPointMap[] = new float[2];
        float rightBottomPointMap[] = new float[2];
        Matrix m1 = new Matrix();
        m1.postRotate(mDegree, mUpperBitmapRect.centerX(), mUpperBitmapRect.centerY());
        m1.mapPoints(leftTopPointMap, leftTopPoint);
        m1.mapPoints(rightTopPointMap, rightTopPoint);
        m1.mapPoints(leftbottomPointMap, leftbottomPoint);
        m1.mapPoints(rightBottomPointMap, rightBottomPoint);

        Line line = new Line(leftTopPointMap[0], -leftTopPointMap[1], rightTopPointMap[0], -rightTopPointMap[1]);
        double x, y;
        if (line.isHrizontal()) {
            x = dx;
            y = 0;
        } else if (line.isVertical()) {
            x = 0;
            y = dx;
        } else {
            int location1 = MathUtil.getLocation(leftTopPointMap[0] - rightTopPointMap[0], leftTopPointMap[1] - rightTopPointMap[1]);
            double degree1 = MathUtil.getDegreeFromX(line, location1);
            if (degree1 < 90) {
                double radians = Math.toRadians(degree1);
                if (dx < 0) {
                    x = -dx * Math.cos(radians);
                    y = dx * Math.sin(radians);
                } else {
                    x = -dx * Math.cos(radians);
                    y = dx * Math.sin(radians);
                }
            } else if (degree1 < 180) {
                double radians = Math.toRadians(180 - degree1);
                if (dx < 0) {
                    x = dx * Math.cos(radians);
                    y = dx * Math.sin(radians);
                } else {
                    x = dx * Math.cos(radians);
                    y = dx * Math.sin(radians);
                }
            } else if (degree1 < 270) {
                double radians = Math.toRadians(270 - degree1);
                if (dx < 0) {
                    x = dx * Math.sin(radians);
                    y = -dx * Math.cos(radians);
                } else {
                    x = dx * Math.sin(radians);
                    y = -dx * Math.cos(radians);
                }
            } else {
                double radians = Math.toRadians(360 - degree1);
                if (dx < 0) {
                    x = -dx * Math.cos(radians);
                    y = -dx * Math.sin(radians);
                } else {
                    x = -dx * Math.cos(radians);
                    y = -dx * Math.sin(radians);
                }
            }
        }
        float leftTopPointMapResult[] = new float[2];
        float leftbottomPointMapResult[] = new float[2];
        leftTopPointMapResult[0] = leftTopPointMap[0] + (float) x;
        leftTopPointMapResult[1] = leftTopPointMap[1] + (float) y;
        leftbottomPointMapResult[0] = leftbottomPointMap[0] + (float) x;
        leftbottomPointMapResult[1] = leftbottomPointMap[1] + (float) y;

        RectF rectF = MathUtil.point2RectF(leftTopPointMapResult, leftbottomPointMapResult, rightTopPointMap, rightBottomPointMap);
        m1.reset();
        m1.postRotate(-mDegree, rectF.centerX(), rectF.centerY());
        m1.mapPoints(leftTopPointMapResult);
        m1.mapPoints(leftbottomPointMapResult);
        m1.mapPoints(rightTopPointMap);
        m1.mapPoints(rightBottomPointMap);
        rectF = MathUtil.point2RectF(leftTopPointMapResult, leftbottomPointMapResult, rightTopPointMap, rightBottomPointMap);

        if (rectF.width() >= SMALLEST_SIZE) {
            mUpperBitmapRect = new RectF(rectF);
            mUpperBitmapRect.left = Math.min(mUpperBitmapRect.left, mUpperBitmapRect.right);
            updateOtherRect();
        }
    }

    public void operationRight(float dx) {
        float leftTopPoint[] = new float[]{mUpperBitmapRect.left, mUpperBitmapRect.top};
        float rightTopPoint[] = new float[]{mUpperBitmapRect.right, mUpperBitmapRect.top};
        float leftbottomPoint[] = new float[]{mUpperBitmapRect.left, mUpperBitmapRect.bottom};
        float rightBottomPoint[] = new float[]{mUpperBitmapRect.right, mUpperBitmapRect.bottom};

        float leftTopPointMap[] = new float[2];
        float rightTopPointMap[] = new float[2];
        float leftbottomPointMap[] = new float[2];
        float rightBottomPointMap[] = new float[2];
        Matrix m1 = new Matrix();
        m1.postRotate(mDegree, mUpperBitmapRect.centerX(), mUpperBitmapRect.centerY());
        m1.mapPoints(leftTopPointMap, leftTopPoint);//计算map后的点
        m1.mapPoints(rightTopPointMap, rightTopPoint);
        m1.mapPoints(leftbottomPointMap, leftbottomPoint);
        m1.mapPoints(rightBottomPointMap, rightBottomPoint);

        Line line = new Line(leftTopPointMap[0], -leftTopPointMap[1], rightTopPointMap[0], -rightTopPointMap[1]);
        double x, y;
        if (line.isHrizontal()) {
            x = dx;
            y = 0;
        } else if (line.isVertical()) {
            x = 0;
            y = dx;
        } else {
            int location1 = MathUtil.getLocation(rightTopPointMap[0] - leftTopPointMap[0], rightTopPointMap[1] - leftTopPointMap[1]);
            double degree1 = MathUtil.getDegreeFromX(line, location1);
            if (degree1 < 90) {
                double radians = Math.toRadians(degree1);
                if (dx < 0) {
                    x = dx * Math.cos(radians);
                    y = -dx * Math.sin(radians);
                } else {
                    x = dx * Math.cos(radians);
                    y = -dx * Math.sin(radians);
                }
            } else if (degree1 < 180) {
                double radians = Math.toRadians(180 - degree1);
                if (dx < 0) {
                    x = -dx * Math.cos(radians);
                    y = -dx * Math.sin(radians);
                } else {
                    x = -dx * Math.cos(radians);
                    y = -dx * Math.sin(radians);
                }
            } else if (degree1 < 270) {
                double radians = Math.toRadians(270 - degree1);
                if (dx < 0) {
                    x = -dx * Math.sin(radians);
                    y = dx * Math.cos(radians);
                } else {
                    x = -dx * Math.sin(radians);
                    y = dx * Math.cos(radians);
                }
            } else {
                double radians = Math.toRadians(360 - degree1);
                if (dx < 0) {
                    x = dx * Math.cos(radians);
                    y = dx * Math.sin(radians);
                } else {
                    x = dx * Math.cos(radians);
                    y = dx * Math.sin(radians);
                }
            }
        }
        float rightTopPointMapResult[] = new float[2];
        float rightbottomPointMapResult[] = new float[2];
        rightTopPointMapResult[0] = rightTopPointMap[0] + (float) x;
        rightTopPointMapResult[1] = rightTopPointMap[1] + (float) y;
        rightbottomPointMapResult[0] = rightBottomPointMap[0] + (float) x;
        rightbottomPointMapResult[1] = rightBottomPointMap[1] + (float) y;

        RectF rectF = MathUtil.point2RectF(leftTopPointMap, leftbottomPointMap, rightTopPointMapResult, rightbottomPointMapResult);
        m1.reset();
        m1.postRotate(-mDegree, rectF.centerX(), rectF.centerY());
        m1.mapPoints(leftTopPointMap);
        m1.mapPoints(leftbottomPointMap);
        m1.mapPoints(rightTopPointMapResult);
        m1.mapPoints(rightbottomPointMapResult);
        rectF = MathUtil.point2RectF(leftTopPointMap, leftbottomPointMap, rightTopPointMapResult, rightbottomPointMapResult);

        if (rectF.width() >= SMALLEST_SIZE) {
            mUpperBitmapRect = new RectF(rectF);
            mUpperBitmapRect.left = Math.min(mUpperBitmapRect.left, mUpperBitmapRect.right);
            updateOtherRect();
        }
    }

    public void operationTop(float dy) {
        float leftTopPoint[] = new float[]{mUpperBitmapRect.left, mUpperBitmapRect.top};
        float rightTopPoint[] = new float[]{mUpperBitmapRect.right, mUpperBitmapRect.top};
        float leftbottomPoint[] = new float[]{mUpperBitmapRect.left, mUpperBitmapRect.bottom};
        float rightBottomPoint[] = new float[]{mUpperBitmapRect.right, mUpperBitmapRect.bottom};

        float leftTopPointMap[] = new float[2];
        float rightTopPointMap[] = new float[2];
        float leftbottomPointMap[] = new float[2];
        float rightBottomPointMap[] = new float[2];
        Matrix m1 = new Matrix();
        m1.postRotate(mDegree, mUpperBitmapRect.centerX(), mUpperBitmapRect.centerY());
        m1.mapPoints(leftTopPointMap, leftTopPoint);//计算map后的点
        m1.mapPoints(rightTopPointMap, rightTopPoint);
        m1.mapPoints(leftbottomPointMap, leftbottomPoint);
        m1.mapPoints(rightBottomPointMap, rightBottomPoint);

        Line line = new Line(leftTopPointMap[0], -leftTopPointMap[1], leftbottomPointMap[0], -leftbottomPointMap[1]);
        double x, y;
        if (line.isHrizontal()) {
            x = dy;
            y = 0;
        } else if (line.isVertical()) {
            x = 0;
            y = dy;
        } else {
            int location1 = MathUtil.getLocation(leftTopPointMap[0] - leftbottomPointMap[0], leftTopPointMap[1] - leftbottomPointMap[1]);
            double degree1 = MathUtil.getDegreeFromX(line, location1);
            if (degree1 < 90) {
                double radians = Math.toRadians(degree1);
                if (dy < 0) {
                    x = -dy * Math.cos(radians);
                    y = dy * Math.sin(radians);
                } else {
                    x = -dy * Math.cos(radians);
                    y = dy * Math.sin(radians);
                }
            } else if (degree1 < 180) {
                double radians = Math.toRadians(180 - degree1);
                if (dy < 0) {
                    x = dy * Math.cos(radians);
                    y = dy * Math.sin(radians);
                } else {
                    x = dy * Math.cos(radians);
                    y = dy * Math.sin(radians);
                }
            } else if (degree1 < 270) {
                double radians = Math.toRadians(270 - degree1);
                if (dy < 0) {
                    x = dy * Math.sin(radians);
                    y = -dy * Math.cos(radians);
                } else {
                    x = dy * Math.sin(radians);
                    y = -dy * Math.cos(radians);
                }
            } else {
                double radians = Math.toRadians(360 - degree1);
                if (dy < 0) {
                    x = -dy * Math.cos(radians);
                    y = -dy * Math.sin(radians);
                } else {
                    x = -dy * Math.cos(radians);
                    y = -dy * Math.sin(radians);
                }
            }
        }
        float leftTopPointMapResult[] = new float[2];
        float rightTopPointMapResult[] = new float[2];
        leftTopPointMapResult[0] = leftTopPointMap[0] + (float) x;
        leftTopPointMapResult[1] = leftTopPointMap[1] + (float) y;
        rightTopPointMapResult[0] = rightTopPointMap[0] + (float) x;
        rightTopPointMapResult[1] = rightTopPointMap[1] + (float) y;

        RectF rectF = MathUtil.point2RectF(leftTopPointMapResult, leftbottomPointMap, rightTopPointMapResult, rightBottomPointMap);
        m1.reset();
        m1.postRotate(-mDegree, rectF.centerX(), rectF.centerY());
        m1.mapPoints(leftTopPointMapResult);
        m1.mapPoints(leftbottomPointMap);
        m1.mapPoints(rightTopPointMapResult);
        m1.mapPoints(rightBottomPointMap);
        rectF = MathUtil.point2RectF(leftTopPointMapResult, leftbottomPointMap, rightTopPointMapResult, rightBottomPointMap);

        if (rectF.width() >= SMALLEST_SIZE) {
            mUpperBitmapRect = new RectF(rectF);
            mUpperBitmapRect.left = Math.min(mUpperBitmapRect.left, mUpperBitmapRect.right);
            updateOtherRect();
        }
    }

    public void operationBottom(float dy) {
        float leftTopPoint[] = new float[]{mUpperBitmapRect.left, mUpperBitmapRect.top};
        float rightTopPoint[] = new float[]{mUpperBitmapRect.right, mUpperBitmapRect.top};
        float leftbottomPoint[] = new float[]{mUpperBitmapRect.left, mUpperBitmapRect.bottom};
        float rightBottomPoint[] = new float[]{mUpperBitmapRect.right, mUpperBitmapRect.bottom};

        float leftTopPointMap[] = new float[2];
        float rightTopPointMap[] = new float[2];
        float leftbottomPointMap[] = new float[2];
        float rightBottomPointMap[] = new float[2];

        Matrix m1 = new Matrix();
        m1.postRotate(mDegree, mUpperBitmapRect.centerX(), mUpperBitmapRect.centerY());
        m1.mapPoints(leftTopPointMap, leftTopPoint);//计算map后的点
        m1.mapPoints(rightTopPointMap, rightTopPoint);
        m1.mapPoints(leftbottomPointMap, leftbottomPoint);
        m1.mapPoints(rightBottomPointMap, rightBottomPoint);

        Line line = new Line(leftbottomPointMap[0], -leftbottomPointMap[1], leftTopPointMap[0], -leftTopPointMap[1]);
        double x, y;
        if (line.isHrizontal()) {
            x = dy;
            y = 0;
        } else if (line.isVertical()) {
            x = 0;
            y = dy;
        } else {
            int location1 = MathUtil.getLocation(leftbottomPointMap[0] - leftTopPointMap[0], leftbottomPointMap[1] - leftTopPointMap[1]);
            double degree1 = MathUtil.getDegreeFromX(line, location1);
            if (degree1 < 90) {
                double radians = Math.toRadians(degree1);
                if (dy < 0) {
                    x = dy * Math.cos(radians);
                    y = -dy * Math.sin(radians);
                } else {
                    x = dy * Math.cos(radians);
                    y = -dy * Math.sin(radians);
                }
            } else if (degree1 < 180) {
                double radians = Math.toRadians(180 - degree1);
                if (dy < 0) {
                    x = -dy * Math.cos(radians);
                    y = -dy * Math.sin(radians);
                } else {
                    x = -dy * Math.cos(radians);
                    y = -dy * Math.sin(radians);
                }
            } else if (degree1 < 270) {
                double radians = Math.toRadians(270 - degree1);
                if (dy < 0) {
                    x = -dy * Math.sin(radians);
                    y = dy * Math.cos(radians);
                } else {
                    x = -dy * Math.sin(radians);
                    y = dy * Math.cos(radians);
                }
            } else {
                double radians = Math.toRadians(360 - degree1);
                if (dy < 0) {
                    x = dy * Math.cos(radians);
                    y = dy * Math.sin(radians);
                } else {
                    x = dy * Math.cos(radians);
                    y = dy * Math.sin(radians);
                }
            }
        }
        float leftBottomPointMapResult[] = new float[2];
        float rightbottomPointMapResult[] = new float[2];
        leftBottomPointMapResult[0] = leftbottomPointMap[0] + (float) x;
        leftBottomPointMapResult[1] = leftbottomPointMap[1] + (float) y;
        rightbottomPointMapResult[0] = rightBottomPointMap[0] + (float) x;
        rightbottomPointMapResult[1] = rightBottomPointMap[1] + (float) y;

        RectF rectF = MathUtil.point2RectF(leftTopPointMap, leftBottomPointMapResult, rightTopPointMap, rightbottomPointMapResult);
        m1.reset();
        m1.postRotate(-mDegree, rectF.centerX(), rectF.centerY());
        m1.mapPoints(leftTopPointMap);
        m1.mapPoints(leftBottomPointMapResult);
        m1.mapPoints(rightTopPointMap);
        m1.mapPoints(rightbottomPointMapResult);
        rectF = MathUtil.point2RectF(leftTopPointMap, leftBottomPointMapResult, rightTopPointMap, rightbottomPointMapResult);

        if (rectF.width() >= SMALLEST_SIZE) {
            mUpperBitmapRect = new RectF(rectF);
            mUpperBitmapRect.left = Math.min(mUpperBitmapRect.left, mUpperBitmapRect.right);
            updateOtherRect();
        }
    }

    public Bitmap getDestBitmap() {
        if (!mIsInit || mBottomBitmap == null || mBottomBitmap.isRecycled() || mUpperBitmap == null || mUpperBitmap.isRecycled()) {
            return mBottomBitmap;
        }
        int width = mBottomBitmap.getWidth();
        int height = mBottomBitmap.getHeight();
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        int layer = -1;
        if (mDrawBottomAgain) {
            canvas.drawBitmap(mBottomBitmap, 0, 0, mBitmapPaint);
            layer = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);
        }
        canvas.drawBitmap(mBottomBitmap, 0, 0, mBitmapPaint);
        canvas.save();
        canvas.scale(width * 1.0f / mBottomBitmapRect.width(), height * 1.0f / mBottomBitmapRect.height());
        RectF rect = new RectF(mUpperBitmapRect);
        rect.offset(-mBottomBitmapRect.left, -mBottomBitmapRect.top);
        canvas.rotate(mDegree, rect.centerX(), rect.centerY());
        mBitmapPaint.setXfermode(mXfermode);
        canvas.drawBitmap(mUpperBitmap, null, rect, mBitmapPaint);
        mBitmapPaint.setXfermode(null);
        canvas.restore();
        if (layer != -1) {
            canvas.restoreToCount(layer);
        }
        return bitmap;
    }

    public Bitmap getEraseBitmap() {
        if (mUpperBitmap != null) {
            return mUpperBitmap.copy(mUpperBitmap.getConfig(), true);
        }
        return null;
    }

    public RectF getEraseBoundRect() {
        return new RectF(mBottomBitmapRect);
    }

    public RectF getEraseContentRect() {
        return new RectF(mUpperBitmapRect);
    }

    public RectF getEraseOriginalRect() {
        return new RectF(mUpperBitmapOriginalRect);
    }

    public float getDegree() {
        return mDegree;
    }

    public void setMixMode(PorterDuff.Mode mode) {
        if (mode != null) {
            mDrawBottomAgain = mode == PorterDuff.Mode.MULTIPLY;
            mXfermode = new PorterDuffXfermode(mode);
        } else {
            mXfermode = null;
            mDrawBottomAgain = false;
        }
        invalidate();
    }

    private OnImageDeleteListener mOnImageDeleteListener;

    public void setOnImageDeleteListener(OnImageDeleteListener onImageDeleteListener) {
        mOnImageDeleteListener = onImageDeleteListener;
    }

    public interface OnImageDeleteListener {
        void onDelete();
    }
}
